package gl.java.network.transport.kcp.udp;

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.DatagramChannel;

@Slf4j
public class UdpServerChildChannel extends AbstractChannel {
    private InetSocketAddress receiver;
    private InetSocketAddress sender;
    private static final ChannelMetadata METADATA = new ChannelMetadata(false);

    @Override
    public InetSocketAddress remoteAddress() {
        return sender;
    }

    @Override
    protected AbstractUnsafe newUnsafe() {
        return new Unsafe();
    }

    @Override
    protected boolean isCompatible(EventLoop eventLoop) {
        return eventLoop instanceof NioEventLoop;
    }

    @Override
    protected SocketAddress localAddress0() {
        return receiver;
    }

    @Override
    protected SocketAddress remoteAddress0() {
        return sender;
    }

    @Override
    protected void doBind(SocketAddress socketAddress) throws Exception {

    }

    @Override
    protected void doDisconnect() throws Exception {

    }

    @Override
    public UdpServerChannel parent() {
        return (UdpServerChannel) super.parent();
    }

    @Override
    protected void doClose() throws Exception {
        parent().doCloseChildChannel(this);
    }

    @Override
    protected void doBeginRead() throws Exception {
    }


    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        log.debug("ServerChildChannel.doWrite {}", in);
        for (; ; ) {
            Object msg = in.current();
            if (msg == null) {
                flushPending = false;
                break;
            }
            parent().send(msg);
            in.remove();
        }
    }

    @Override
    public ChannelConfig config() {
        return parent().config();
    }

    @Override
    public boolean isOpen() {
        return parent().isActive();
    }

    @Override
    public boolean isActive() {
        return parent().isActive();
    }

    @Override
    public ChannelMetadata metadata() {
        return METADATA;
    }

    @Override
    public InetSocketAddress localAddress() {
        return receiver;
    }

    private boolean flushPending;

    boolean isFlushPending() {
        return flushPending;
    }

    final class Unsafe extends AbstractUnsafe {

        @Override
        public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
            safeSetFailure(promise, new UnsupportedOperationException());
        }

        @Override
        protected void flush0() {
            log.info("UdpServerChild.Unsafe.flush0");
            if (isFlushPending()) {
                return;
            }
            super.flush0();
        }

        void forceFlush() {
            super.flush0();
        }


    }

    public UdpServerChildChannel(UdpServerChannel parent, DatagramChannel datagramChannel, DatagramPacket packet) {
        super(parent);
        this.sender = packet.sender();
        this.receiver = packet.recipient();

    }

}
